"""
Redis客户端工具
"""
import json
import asyncio
from typing import Optional, Any, Dict, List
from redis import asyncio as aioredis
from loguru import logger

from app.core.config import settings


class RedisClient:
    """Redis客户端类"""
    
    def __init__(self):
        self.redis: Optional[aioredis.Redis] = None
        self.pubsub: Optional[aioredis.client.PubSub] = None
    
    async def connect(self):
        """连接Redis"""
        try:
            self.redis = aioredis.from_url(
                settings.redis_url,
                encoding="utf-8",
                decode_responses=True
            )
            # 测试连接
            await self.redis.ping()
            logger.info("Redis连接成功")
        except Exception as e:
            logger.warning(f"Redis连接失败，将在无缓存模式下运行: {e}")
            self.redis = None
    
    async def disconnect(self):
        """断开Redis连接"""
        if self.pubsub:
            await self.pubsub.close()
        if self.redis:
            await self.redis.close()
        logger.info("Redis连接已断开")
    
    async def set(self, key: str, value: Any, expire: Optional[int] = None) -> bool:
        """设置键值"""
        if not self.redis:
            return False

        try:
            if isinstance(value, (dict, list)):
                value = json.dumps(value, ensure_ascii=False)

            result = await self.redis.set(key, value, ex=expire)
            return result
        except Exception as e:
            logger.error(f"Redis设置失败: {e}")
            return False
    
    async def get(self, key: str) -> Optional[Any]:
        """获取值"""
        if not self.redis:
            return None

        try:
            value = await self.redis.get(key)
            if value is None:
                return None

            # 尝试解析JSON
            try:
                return json.loads(value)
            except (json.JSONDecodeError, TypeError):
                return value
        except Exception as e:
            logger.error(f"Redis获取失败: {e}")
            return None
    
    async def delete(self, key: str) -> bool:
        """删除键"""
        try:
            result = await self.redis.delete(key)
            return result > 0
        except Exception as e:
            logger.error(f"Redis删除失败: {e}")
            return False
    
    async def exists(self, key: str) -> bool:
        """检查键是否存在"""
        try:
            result = await self.redis.exists(key)
            return result > 0
        except Exception as e:
            logger.error(f"Redis检查存在失败: {e}")
            return False
    
    async def expire(self, key: str, seconds: int) -> bool:
        """设置过期时间"""
        try:
            result = await self.redis.expire(key, seconds)
            return result
        except Exception as e:
            logger.error(f"Redis设置过期时间失败: {e}")
            return False
    
    async def hset(self, name: str, mapping: Dict[str, Any]) -> bool:
        """设置哈希表"""
        try:
            # 将字典值转换为JSON字符串
            json_mapping = {}
            for k, v in mapping.items():
                if isinstance(v, (dict, list)):
                    json_mapping[k] = json.dumps(v, ensure_ascii=False)
                else:
                    json_mapping[k] = str(v)
            
            result = await self.redis.hset(name, mapping=json_mapping)
            return result > 0
        except Exception as e:
            logger.error(f"Redis哈希设置失败: {e}")
            return False
    
    async def hget(self, name: str, key: str) -> Optional[Any]:
        """获取哈希表值"""
        try:
            value = await self.redis.hget(name, key)
            if value is None:
                return None
            
            # 尝试解析JSON
            try:
                return json.loads(value)
            except (json.JSONDecodeError, TypeError):
                return value
        except Exception as e:
            logger.error(f"Redis哈希获取失败: {e}")
            return None
    
    async def hgetall(self, name: str) -> Dict[str, Any]:
        """获取所有哈希表值"""
        try:
            result = await self.redis.hgetall(name)
            
            # 尝试解析JSON值
            parsed_result = {}
            for k, v in result.items():
                try:
                    parsed_result[k] = json.loads(v)
                except (json.JSONDecodeError, TypeError):
                    parsed_result[k] = v
            
            return parsed_result
        except Exception as e:
            logger.error(f"Redis哈希获取所有失败: {e}")
            return {}
    
    async def hdel(self, name: str, *keys: str) -> bool:
        """删除哈希表键"""
        try:
            result = await self.redis.hdel(name, *keys)
            return result > 0
        except Exception as e:
            logger.error(f"Redis哈希删除失败: {e}")
            return False
    
    async def lpush(self, name: str, *values: Any) -> bool:
        """列表左推"""
        try:
            json_values = []
            for value in values:
                if isinstance(value, (dict, list)):
                    json_values.append(json.dumps(value, ensure_ascii=False))
                else:
                    json_values.append(str(value))
            
            result = await self.redis.lpush(name, *json_values)
            return result > 0
        except Exception as e:
            logger.error(f"Redis列表推入失败: {e}")
            return False
    
    async def rpop(self, name: str) -> Optional[Any]:
        """列表右弹出"""
        try:
            value = await self.redis.rpop(name)
            if value is None:
                return None
            
            # 尝试解析JSON
            try:
                return json.loads(value)
            except (json.JSONDecodeError, TypeError):
                return value
        except Exception as e:
            logger.error(f"Redis列表弹出失败: {e}")
            return None
    
    async def lrange(self, name: str, start: int, end: int) -> List[Any]:
        """获取列表范围"""
        try:
            values = await self.redis.lrange(name, start, end)
            
            # 尝试解析JSON值
            parsed_values = []
            for value in values:
                try:
                    parsed_values.append(json.loads(value))
                except (json.JSONDecodeError, TypeError):
                    parsed_values.append(value)
            
            return parsed_values
        except Exception as e:
            logger.error(f"Redis列表范围获取失败: {e}")
            return []
    
    async def publish(self, channel: str, message: Any) -> bool:
        """发布消息"""
        try:
            if isinstance(message, (dict, list)):
                message = json.dumps(message, ensure_ascii=False)
            
            result = await self.redis.publish(channel, message)
            return result > 0
        except Exception as e:
            logger.error(f"Redis发布消息失败: {e}")
            return False
    
    async def subscribe(self, *channels: str):
        """订阅频道"""
        try:
            if not self.pubsub:
                self.pubsub = self.redis.pubsub()
            
            await self.pubsub.subscribe(*channels)
            logger.info(f"订阅频道: {channels}")
        except Exception as e:
            logger.error(f"Redis订阅失败: {e}")
            raise
    
    async def get_message(self, timeout: Optional[float] = None) -> Optional[Dict]:
        """获取订阅消息"""
        try:
            if not self.pubsub:
                return None
            
            message = await self.pubsub.get_message(timeout=timeout)
            if message and message['type'] == 'message':
                try:
                    message['data'] = json.loads(message['data'])
                except (json.JSONDecodeError, TypeError):
                    pass
                return message
            return None
        except Exception as e:
            logger.error(f"Redis获取消息失败: {e}")
            return None


# 全局Redis客户端实例
redis_client = RedisClient()


async def init_redis():
    """初始化Redis连接"""
    await redis_client.connect()


async def close_redis():
    """关闭Redis连接"""
    await redis_client.disconnect()


# 缓存装饰器
def cache_result(key_prefix: str, expire: int = 3600):
    """缓存结果装饰器"""
    def decorator(func):
        async def wrapper(*args, **kwargs):
            # 生成缓存键
            cache_key = f"{key_prefix}:{hash(str(args) + str(kwargs))}"
            
            # 尝试从缓存获取
            cached_result = await redis_client.get(cache_key)
            if cached_result is not None:
                logger.debug(f"缓存命中: {cache_key}")
                return cached_result
            
            # 执行函数并缓存结果
            result = await func(*args, **kwargs)
            await redis_client.set(cache_key, result, expire)
            logger.debug(f"缓存设置: {cache_key}")
            
            return result
        return wrapper
    return decorator
